package minov.parser;

import java.util.Deque;
import java.util.Vector;

import minov.parser.utils.StringUtils;

/**
 * {@class ArgumentObject}
 */
public abstract class ArgumentObject {

   // --------------------------------------------
   // Argument aliases
   // --------------------------------------------

   /**
    * Collection for keeping all aliases of this argument
    */
   private Vector<String> _argumentNames = new Vector<String>();

   /**
    * 
    * @return argument aliases
    */
   public final String[] getArgumentNames() {
      return _argumentNames.toArray(new String[0]);
   }

   /**
    * Add argument aliases. Function register this class at owner parser under
    * entered aliases. Parser will split entered aliases into full names and
    * short names. Short names are aliases that contains only one character.
    * @param [in] argumentAliases the aliases to be registered to the argument
    * 
    */
   public final void addArgumentAliases(String... argumentAliases) {

      for (String argumentAlias : argumentAliases) {
         if (!StringUtils.isEmptyString(argumentAlias)) {
            _argumentNames.add(argumentAlias);
         }
      }
   }

   // --------------------------------------------
   // Owner argument parser
   // --------------------------------------------

   /**
    * Parent argument parser, that registers this class.
    */
   private ArgumentParser _ownerParser = null;

   /**
    * Set parent argument parser. This method is called form argument parser at
    * registration.
    * @param [in] parser the parser to be registered to.
    */
   public final void setOwnerParser(ArgumentParser parser) {
      _ownerParser = parser;
   }

   // --------------------------------------------
   // Argument value count
   // --------------------------------------------

   /**
    * No argument count
    */
   public static int NO_ARGUMENT = 0;

   /**
    * One argument count
    */
   public static int ONE_ARGUMENT = 1;

   /**
    * Unlimited argument count
    */
   public static int UNLIMITED_ARGUMENT = Integer.MAX_VALUE;

   /**
    * Minimal count of entered values
    */
   private int _minimalValueCount = NO_ARGUMENT;

   /**
    * Maximal count of entered values
    */
   private int _maximalValueCount = ONE_ARGUMENT;

   /**
    * Set conditions, that value of this argument is optional. It means that
    * value of this argument does not have to be set, but it can. It sets
    * minimal values count to 0 and maximal 1.
    * <p>
    * Equivalent function is setValueCountRange(0,1)
    * <p>
    */
   public final void setValueAsOptional() {
      setValueCountRange(NO_ARGUMENT, ONE_ARGUMENT);
   }

   /**
    * Set conditions, that value of this is required. It means that value of
    * this argument has to be set. Minimal argument values count is 1 and
    * maximal 1.
    * <p>
    * Equivalent function is setValueCountRange(1,1)
    * <p>
    */
   public final void setValueAsRequired() {
      setValueCountRange(ONE_ARGUMENT, ONE_ARGUMENT);
   }

   /**
    * Method for setting the required number of parameter values for the argument.
    * @param [in] minimalCount minimal number of the parameters
    * @param [in] maximalCount maximal number of the parameters
    */
   public final void setValueCountRange(int minimalCout, int maximalCount) {
      this._minimalValueCount = Math.max(minimalCout, 0);
      this._maximalValueCount = Math.max(maximalCount, this._minimalValueCount);

   }

   /**
   * Method for getting the minimal required number of parameter values for the argument.
   * @param [in] return the minimal number of parameters
   */
   public final int getMinimalCount() {
      return _minimalValueCount;
   }

   /**
   * Method for getting the maximal required number of parameter values for the argument.
   * @param [in] return the maximal number of parameters
   */
   public final int getMaximalCount() {
      return _maximalValueCount;
   }

   // --------------------------------------------
   // Argument occurrence
   // --------------------------------------------

   private boolean _isMandatory = false;

   /**
    * Method for checking whether the argument is mandatory.
    * @return flag whether the argument is mandatory
    */
   public boolean isMandatory() {
      return _isMandatory;
   }

   /**
    * Method for setting the argument as optional - the argument doesn't have to be used when calling the program.
    */
   public final void setArgumentAsOptional() {
      _isMandatory = false;
   }

   /**
    * Method for setting the argument as mandatory - the argument must be used when calling the program.
    */
   public final void setArgumentAsMandatory() {
      _isMandatory = true;
   }

   // --------------------------------------------
   // Flag whether the argument was entered, and its access methods
   // --------------------------------------------

   private boolean _argumentIsEntered = false;

   /**
    * Set this argument as entered.
    * <p>
    * This method should be called from parsing method.
    * </p>
    * 
    * @see isEntered
    * @see parseArguments
    */
   public final void setAsEntered() {
      _argumentIsEntered = true;
   }

   /**
    * 
    * @return True, if argument was entered, otherwise return false.
    */
   public final boolean isEntered() {
      return _argumentIsEntered;

   }

   private int _enteredValues = 0;

   /**
    * Method for setting the number of the entered parameter values of the argument. Flags the argument as entered.
    * @param [in] valuesCount number of the parameters
    */
   public final void setEnteredValues(int valuesCount) {
      _enteredValues = valuesCount;
      this.setAsEntered();
   }

   /**
    * Method for getting the number of the entered parameter values of the argument.
    * @return number of the parameters
    */
   public final int getEnteredValues() {
      return _enteredValues;
   }

   // --------------------------------------------
   // Argument Help
   // --------------------------------------------

   /**
    * Method for getting the default name or id of the argument.
    * @return the default name
    */
   public final String getDefaultName() {
      String firstShortName = "";
      for (String name : _argumentNames) {
         if (name.length() > 1) {
            return name;
         }
         firstShortName = name;
      }
      if (firstShortName.isEmpty()) {
         return _ownerParser.getArgumentId(this);
      }
      return firstShortName;
   }

   private String _shortHelpDescription = null;
   private String _fullHelpDescription = null;

   /**
    * Method for adding help descriptions for the argument.
    * @param [in] shortHelpDescription the short help description
    * @param [in] shortHelpDescription the full help description
    */
   public final void setHelpDescription(String shortHelpDescription,
         String _fullHelpDescription) {
      this._shortHelpDescription = shortHelpDescription;
      this._fullHelpDescription = _fullHelpDescription;
   }

   /**
    * Function for getting the short help description for the argument.
    * @return the help description
    */
   public final String getShortHelpDescription() {
      return _shortHelpDescription;
   }

   /**
    * Function for getting the full help description for the argument.
    * @return the help description
    */
   public final String getFullHelpDescription() {
      return _fullHelpDescription;
   }

   // --------------------------------------------
   // Individual methods
   // --------------------------------------------

   private static String MISSING_MANDATORY_ARGUMENT =
         "Mandatory argument %s is missing";
   private static String NOT_ENOUGHT_VALUES =
         "Wrong values count of argument %s." + " Enter at least %d values.";
   private static String TOO_MANY_VALUES = "Wrong values count of argument %s."
         + " Enter at most %d values.";

   /**
    * Function for validating the argument
    * @return ValidationResult object with the result
    */
   public ValidationResult validateArgument() {

      if (isMandatory() && isEntered() == false) {
         return ValidationResult.createInvalidResponse(String.format(
               MISSING_MANDATORY_ARGUMENT, getDefaultName()));
      }

      if (_enteredValues < _minimalValueCount) {

         return ValidationResult.createInvalidResponse(String.format(
               NOT_ENOUGHT_VALUES, getDefaultName(), _minimalValueCount));
      }

      if (_enteredValues > _maximalValueCount) {
         return ValidationResult.createInvalidResponse(String.format(
               TOO_MANY_VALUES, getDefaultName(), _minimalValueCount));
      }

      return ValidationResult.createValidResponse();
   }

   /**
    * Method for processing the parameters. Used by the parser.
    */
   public abstract void parseArguments(Deque<ParsedArgumentData> arguments);

   /**
    * Resets the ArgumentObject for further use.
    */
   public void reset() {
      _enteredValues = 0;
      _argumentIsEntered = false;
   }
}
